package com.acgist.snail.net.torrent.tracker;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;

import com.acgist.snail.config.SystemConfig;
import com.acgist.snail.config.TrackerConfig.Action;
import com.acgist.snail.logger.Logger;
import com.acgist.snail.logger.LoggerFactory;
import com.acgist.snail.net.UdpMessageHandler;
import com.acgist.snail.utils.ByteUtils;
import com.acgist.snail.utils.PeerUtils;

/**
 * UDP Tracker消息代理
 * 
 * @author acgist
 */
public final class TrackerMessageHandler extends UdpMessageHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(TrackerMessageHandler.class);
    
    /**
     * 连接消息最小长度：{@value}
     */
    private static final int CONNECT_MIN_LENGTH = 12;
    /**
     * 声明消息最小长度：{@value}
     */
    private static final int ANNOUNCE_MIN_LENGTH = 16;
    /**
     * 刮擦消息最小长度：{@value}
     */
    private static final int SCRAPE_MIN_LENGTH = 16;
    
    /**
     * 服务端
     */
    public TrackerMessageHandler() {
        this(null);
    }
    
    /**
     * 客户端
     * 
     * @param socketAddress 地址
     */
    public TrackerMessageHandler(InetSocketAddress socketAddress) {
        super(socketAddress);
    }
    
    @Override
    public void onReceive(ByteBuffer buffer, InetSocketAddress socketAddress) {
        final int remaining = buffer.remaining();
        if(remaining < Integer.BYTES) {
            LOGGER.warn("处理UDP Tracker消息错误（长度）：{}", remaining);
            return;
        }
        final int id = buffer.getInt();
        final var action = Action.of(id);
        if(action == null) {
            LOGGER.warn("处理UDP Tracker消息错误（未知动作）：{}", id);
            return;
        }
        switch (action) {
            case CONNECT -> this.doConnect(buffer);
            case ANNOUNCE -> this.doAnnounce(buffer);
            case SCRAPE -> this.doScrape(buffer);
            case ERROR -> this.doError(buffer);
            default -> LOGGER.debug("处理UDP Tracker消息错误（动作未适配）：{}", action);
        }
    }

    /**
     * 连接消息
     * 
     * @param buffer 消息
     */
    private void doConnect(ByteBuffer buffer) {
        final int remaining = buffer.remaining();
        if(remaining < CONNECT_MIN_LENGTH) {
            LOGGER.debug("处理UDP Tracker连接消息错误（长度）：{}", remaining);
            return;
        }
        final int trackerId = buffer.getInt();
        final long connectionId = buffer.getLong();
        TrackerContext.getInstance().connectionId(trackerId, connectionId);
    }

    /**
     * 声明消息
     * 
     * @param buffer 消息
     */
    private void doAnnounce(ByteBuffer buffer) {
        final int remaining = buffer.remaining();
        if(remaining < ANNOUNCE_MIN_LENGTH) {
            LOGGER.debug("处理UDP Tracker声明消息错误（长度）：{}", remaining);
            return;
        }
        final AnnounceMessage message = AnnounceMessage.newUdp(
            buffer.getInt(),
            buffer.getInt(),
            buffer.getInt(),
            buffer.getInt(),
            // 根据本地IP地址返回相同类型Peer
            SystemConfig.getExternalIPAddressIPv4() ? PeerUtils.readIPv4(buffer) : PeerUtils.readIPv6(buffer)
        );
        TrackerContext.getInstance().announce(message);
    }
    
    /**
     * 刮擦消息
     * 
     * @param buffer 消息
     */
    private void doScrape(ByteBuffer buffer) {
        final int remaining = buffer.remaining();
        if(remaining < SCRAPE_MIN_LENGTH) {
            LOGGER.debug("处理UDP Tracker刮擦消息错误（长度）：{}", remaining);
            return;
        }
        final ScrapeMessage message = ScrapeMessage.newInstance(
            buffer.getInt(),
            buffer.getInt(),
            buffer.getInt(),
            buffer.getInt()
        );
        TrackerContext.getInstance().scrape(message);
    }

    /**
     * 错误消息
     * 
     * @param buffer 消息
     */
    private void doError(ByteBuffer buffer) {
        final int remaining = buffer.remaining();
        if(remaining < Integer.BYTES) {
            LOGGER.debug("处理UDP Tracker错误消息错误（长度）：{}", remaining);
            return;
        }
        final var trackerId = buffer.getInt();
        final String message = ByteUtils.remainingToString(buffer);
        LOGGER.warn("UDP Tracker错误消息：{}-{}", trackerId, message);
    }

}
